package gradation.transformation;

import gradation.GrayScaleTransformation;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import math.Equation;
import math.MathFactory;
import core.ConstantValue;
import debug.DebugLogger;

public class GrayPiecewiseTransformation extends GrayScaleTransformation {

	private static final PiecewisePoint ORIGINAL_POINT = new PiecewisePoint(0,
			0);

	private boolean generated = false;
	private List<PiecewisePoint> points = new ArrayList<>();
	private Integer[] valueArray = new Integer[ConstantValue.SMALE_MAX_AMOUNT];
	private HashMap<Integer, Equation> equations = new HashMap<>();

	public GrayPiecewiseTransformation() {
		reset();
	}

	public void addPiecewisePoint(PiecewisePoint point) {
		points.add(point);
	}

	@Override
	public int[] processPixel(int[] pixel, int x, int y) {
		generateValueArray();

		for (int i = 0; i < pixel.length; i++) {
			pixel[i] = valueArray[pixel[i]];
		}
		
		return pixel;
	}

	private void generateValueArray() {
		if (generated) {
			return;
		}
		generated = true;

		Collections.sort(points);

		for (int i = 1; i < points.size(); i++) {
			PiecewisePoint p1 = points.get(i);
			PiecewisePoint p0 = points.get(i - 1);

			double slope = p1.calculateSlope(p0);

			Equation e = MathFactory.getOneUnknownLinearEquation(slope,
					(p0.getOutput() - slope * p0.getInput()));
			equations.put(p1.getInput(), e);

			if (i == 1) {
				equations.put(0, e);
			}
		}

		for (int i = 0; i <= ConstantValue.SMALE_MAX_VALUE; i++) {
			for (PiecewisePoint p : points) {
				if (i <= p.getInput()) {
					valueArray[i] = (int) equations.get(p.getInput())
							.calculate(i);
					break;
				}
			}
		}

		DebugLogger.log(valueArray);

		for (Integer key : equations.keySet()) {
			DebugLogger.log(key + " " + equations.get(key).toString());
		}
	}

	@Override
	public void reset() {
		generated = false;
		equations.clear();
		points.clear();
		points.add(ORIGINAL_POINT);
	}

}
